package cn.android666.recorddemowithnoise

import android.content.Context
import android.media.AudioAttributes
import android.media.AudioFormat
import android.media.AudioManager
import android.media.AudioTrack
import android.os.Handler
import android.os.Looper
import android.os.Process
import android.widget.Toast
import kotlin.concurrent.thread

const val USE_VOICE_CALL = true
val STREAM_TYPE = if (USE_VOICE_CALL) AudioManager.STREAM_VOICE_CALL else AudioManager.STREAM_MUSIC

class PcmPlayer {

    @Volatile
    private var play = false
    private var mAudioTrack: AudioTrack? = null

    /** 初始化时打开扬声器，并设置音量大小 */
    private fun initVolume() {
        val audioManager = App.app.getSystemService(Context.AUDIO_SERVICE) as AudioManager
        val volume = audioManager.getStreamMaxVolume(STREAM_TYPE)
//        val volume = 2
        audioManager.setStreamVolume(STREAM_TYPE, volume, 1)// 通话类型音量范围：1 ~ 7，音乐类型：0 ~ 15

        Speakerphone.on() // 打开扬声器

        if (USE_VOICE_CALL) {
            // 注：如果是通话类型，必须在打开扬声器后再设置一次音量值，因为之前设置的是听筒的音量值
            audioManager.setStreamVolume(STREAM_TYPE, volume, 1)
        }
        printLog("播放器初始化，播放的声音类型为${if (USE_VOICE_CALL) "通话" else "音乐"}，已设置音量最大化为：$volume")
    }

    fun startPlay(isPlayWhiteNoise: Boolean, addDB: Int = 0) {
        if (play) {
            stopPlay()
            Thread.sleep(300) // 等300ms，等停止完成，卡主线程就卡吧，没事，赖得写那么复杂了
        }

        val sampleRateInHz = 8000

        // setFlags() 方法用于设置音频属性的标志。这些标志可以影响音频的行为或处理方式。
        // AudioAttributes.FLAG_AUDIBILITY_ENFORCED 表示音频内容的可听性是强制执行的。这意味着系统会尽可能地确保用户能够听到这些音频内容，即使在一些限制了音频可听性的环境下，例如在噪音较大的环境中。
        // 这个标志通常用于一些对音频可听性要求较高的场景，比如紧急广播、语音导航等。通过设置这个标志，可以确保这些音频内容能够在各种环境下被清晰地听到，提高用户体验和安全性。
        val attributes = AudioAttributes.Builder()
            .setUsage(if (USE_VOICE_CALL) AudioAttributes.USAGE_VOICE_COMMUNICATION else AudioAttributes.USAGE_MEDIA)
            .setContentType(if (USE_VOICE_CALL) AudioAttributes.CONTENT_TYPE_SPEECH else AudioAttributes.CONTENT_TYPE_MUSIC)
            .setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED)
            .build()

        val format = AudioFormat.Builder()
            .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
            .setSampleRate(sampleRateInHz)
            .setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
            .build()

        val audioManager = App.app.getSystemService(Context.AUDIO_SERVICE) as AudioManager
        val sessionId = audioManager.generateAudioSessionId()
        val minBufferSize = AudioTrack.getMinBufferSize(sampleRateInHz, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT)

        val audioTrack = AudioTrack(attributes, format, minBufferSize, AudioTrack.MODE_STREAM, sessionId)
        // 下面的构造方式已过时(在北京的执法仪上，用下面这个构造函数播放白噪音很大声，用上面的构造函数播又很小声，奇怪！)
//        val audioTrack = AudioTrack(STREAM_TYPE, sampleRateInHz, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize, AudioTrack.MODE_STREAM)

        // 注：当用新的构造函数AudioTrack(attributes,...)的构造函数时，传统的设置音量函数已不起作用: audioManager.setStreamVolume()
        // 需要改用mAudioTrack.setVolume()来设置，范围为0.0f ~ 1.0f，其实就是一个百分比，1.0就是100%，就是音量最大化。
        // 注：当是AudioAttributes.USAGE_MEDIA类型时，0.0f ~ 1.0f是以当前系统音量为基准的百分比，比如用户设置系统音量为50%，我们这里调传0.5参数，那么音量大小实际相当于系统音量的25%了，也就是说
        // 这种情况下，我们只能把音量调的比当前系统音量小，不能调大。所以，这种情况下，如果想要音量绝对的最大化，可以用audioManager.setStreamVolume()设置为最大，再用audioTrack.setVolume(1.0f)来设置音量最大。
        // 而是AudioAttributes.USAGE_VOICE_COMMUNICATION类型时，则不受系统音量影响，设置1.0，则为最大的声音，不用和系统设置的音量挂勾了。
        //audioTrack.setVolume(1.0f) // 在北京执法仪设备上，调用这个函数后录音效果会变得非常差，原因不详。

        mAudioTrack = audioTrack

        if (audioTrack.state != AudioTrack.STATE_INITIALIZED) {
            printLog("播放杂音失败：初始化AudioTrack失败")
            return
        }

        // 必须在AudioTrack之后调用，如果在之前调用的话，在有的手机设置音量大小是OK的，有的手机则不起作用。
        initVolume()

        thread { startPlay(audioTrack, isPlayWhiteNoise, addDB) }
    }

    private var currentVolume = 1.0f

    fun volumeUp() {
        val audioTrack = mAudioTrack ?: return
        if (currentVolume >= 1.0f) return
        currentVolume += 0.1f
        audioTrack.setVolume(currentVolume)
    }

    fun volumeDown() {
        val audioTrack = mAudioTrack ?: return
        if (currentVolume <= 0.0f) return
        currentVolume -= 0.1f
        audioTrack.setVolume(currentVolume)
    }

    fun stopPlay() {
        play = false
        mAudioTrack = null
        println("已调用停止播放")
    }

    private fun startPlay(audioTrack: AudioTrack, isPlayWhiteNoise: Boolean, addDB: Int) {
        printLog("开始播放PCM文件")
        Process.setThreadPriority(Process.THREAD_PRIORITY_AUDIO)
        play = true
        val db = if (isPlayWhiteNoise) addDB * 5 else 0
//        val pcmFile = if (isPlayWhiteNoise)  else "8k_baiyang.pcm"
        val pcmFile = if (isPlayWhiteNoise) "8k_noise_plus_${db}dB.pcm" else "8k_baiyang.pcm"
        showToast("开始播放：$pcmFile")
        audioTrack.play()

        App.app.assets.open(pcmFile).use { input ->
            val buf = ByteArray(1024)
            var len: Int
            while (input.read(buf).also { len = it } > 0 && play) {
                audioTrack.write(buf, 0, len)
            }
        }

        audioTrack.stop()
        audioTrack.release()
        printLog("播放结束")
    }

    private fun showToast(message: String) {
        Handler(Looper.getMainLooper()).post {
            Toast.makeText(App.app, message, Toast.LENGTH_SHORT).show()
        }
    }
}